package com.inspinia.base.util;

import com.google.common.net.HttpHeaders;
import com.inspinia.base.common.model.BaseConstants;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.apache.http.*;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.util.*;

/**
 * HttpClient连接池基础类
 * 参考：https://yq.aliyun.com/articles/294
 * Created by WangJun on 16/5/6.
 */
public class HttpClientUtil {

    static final int timeOut = 120 * 1000;

    private static CloseableHttpClient httpClient = null;

    private final static Object syncLock = new Object();
    //最大连接数
    private static final int maxTotal = 2048;
    //每个路由基础的连接
    private static final int maxPerRoute = 20;
    //模拟浏览器的header
    public static final Map<String, String> simulateHeaderMap = new HashMap<>();
    public static RequestConfig defaultRequestConfig;

    static {
        initSimulateHeaderMap();
        initDefaultRequestConfig();
    }

    private static void initDefaultRequestConfig() {
        // 配置请求的超时设置
        defaultRequestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(timeOut)
                .setConnectTimeout(timeOut).setSocketTimeout(timeOut).build();
    }

    public static void initSimulateHeaderMap() {
        simulateHeaderMap.put("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
        simulateHeaderMap.put("Accept-Language", "en-us,en;q=0.7,zh-cn;q=0.3");
        simulateHeaderMap.put("Accept-Encoding", "utf-8");
        simulateHeaderMap.put("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
        simulateHeaderMap.put("Keep-Alive", "300");
        simulateHeaderMap.put("connnection", "keep-alive");
        simulateHeaderMap.put("If-Modified-Since", "Fri, 02 Jan 2009 17:00:05 GMT");
        simulateHeaderMap.put("If-None-Match", "1261d8-4290-df64d224");
        simulateHeaderMap.put("Cache-conntrol", "max-age=0");
        simulateHeaderMap.put("Referer", "http://www.baidu.com");
        simulateHeaderMap.put("Content-Type","application/json");
    }


    /**
     * 获取HttpClient对象
     *
     * @return
     * @author SHANHY
     * @create 2015年12月18日
     */
    public static CloseableHttpClient getHttpClient() {
        if (httpClient == null) {
            synchronized (syncLock) {
                if (httpClient == null) {
                    httpClient = createHttpClient(maxTotal, maxPerRoute, null);
                }
            }
        }
        return httpClient;
    }

    /**
     * 创建http对象
     *
     * @param maxTotal                最大连接数
     * @param maxPerRoute             每个路由基础的连接
     * @param httpRequestRetryHandler 重试处理
     * @return
     */
    public static CloseableHttpClient createHttpClient(int maxTotal,
                                                       int maxPerRoute, HttpRequestRetryHandler httpRequestRetryHandler) {
        ConnectionSocketFactory plainsf = PlainConnectionSocketFactory
                .getSocketFactory();
        LayeredConnectionSocketFactory sslsf = SSLConnectionSocketFactory
                .getSocketFactory();
        Registry<ConnectionSocketFactory> registry = RegistryBuilder
                .<ConnectionSocketFactory>create().register("http", plainsf)
                .register("https", sslsf).build();
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(
                registry);
        cm.setMaxTotal(maxTotal);
        cm.setDefaultMaxPerRoute(maxPerRoute);

        if (httpRequestRetryHandler == null) {
            // 请求重试处理
            httpRequestRetryHandler = new HttpRequestRetryHandler() {
                @Override
                public boolean retryRequest(IOException exception,
                                            int executionCount, HttpContext context) {
                    if (executionCount >= 5) {// 如果已经重试了5次，就放弃
                        return false;
                    }
                    if (exception instanceof NoHttpResponseException) {// 如果服务器丢掉了连接，那么就重试
                        return true;
                    }
                    if (exception instanceof SSLHandshakeException) {// 不要重试SSL握手异常
                        return false;
                    }
                    if (exception instanceof InterruptedIOException) {// 超时
                        return false;
                    }
                    if (exception instanceof UnknownHostException) {// 目标服务器不可达
                        return false;
                    }
                    if (exception instanceof ConnectTimeoutException) {// 连接被拒绝
                        return false;
                    }
                    if (exception instanceof SSLException) {// SSL握手异常
                        return false;
                    }

                    HttpClientContext clientContext = HttpClientContext
                            .adapt(context);
                    HttpRequest request = clientContext.getRequest();
                    // 如果请求是幂等的，就再次尝试
                    if (!(request instanceof HttpEntityEnclosingRequest)) {
                        return true;
                    }
                    return false;
                }
            };
        }

        CloseableHttpClient httpClient = HttpClients.custom()
                .setConnectionManager(cm)
                .setRetryHandler(httpRequestRetryHandler).build();

        return httpClient;
    }


    /**
     * 设置模拟浏览器的header
     *
     * @param httpRequestBase
     */
    public static void setSimulateHeader(HttpRequestBase httpRequestBase) {
        simulateHeaderMap.forEach((k, v) -> {
            httpRequestBase.setHeader(k, v);
        });
    }


    private static void setPostParams(HttpPost httpost,
                                      Map<String, Object> params) throws UnsupportedEncodingException {
        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        Set<String> keySet = params.keySet();
        for (String key : keySet) {
            nvps.add(new BasicNameValuePair(key, params.get(key).toString()));
        }
        httpost.setEntity(new UrlEncodedFormEntity(nvps, "UTF-8"));
    }

    /**
     * post请求URL获取内容
     *
     * @param url
     * @return
     * @throws IOException
     */
    public static String post(String url, Map<String, Object> params) throws IOException {
        HttpPost httppost = new HttpPost(url);
        httppost.setConfig(defaultRequestConfig);
        setPostParams(httppost, params);
        return getResponseStringContent(httppost);
    }


    /**
     * post请求URL获取内容
     *
     * @param url
     * @param headerMap     请求头参数
     * @param requestEntity
     * @return
     * @throws IOException
     */
    public static String post(String url, Map<String, Object> params, Map<String, String> headerMap, HttpEntity requestEntity) throws IOException {
        HttpPost httppost = new HttpPost(url);
        httppost.setConfig(defaultRequestConfig);
        setPostParams(httppost, params);
        if (headerMap != null) {
            headerMap.forEach((k, v) -> {
                httppost.setHeader(k, v);
            });
        }
        if (requestEntity != null) {
            httppost.setEntity(requestEntity);
        }
        return getResponseStringContent(httppost);
    }


    /**
     * post请求URL获取内容
     *
     * @param url
     * @param jsonParam json格式的请求参数
     * @param headerMap 请求头参数
     * @return
     * @throws IOException
     */
    public static String postJson(String url, String jsonParam, Map<String, String> headerMap) throws IOException {
        HttpPost httpPost = new HttpPost(url);
        httpPost.setConfig(defaultRequestConfig);
        if (headerMap != null) {
            headerMap.forEach((k, v) -> {
                httpPost.setHeader(k, v);
            });
        }
        setJsonRequestConfig(httpPost, jsonParam);
        return getJsonStringResponseContent(httpPost);
    }

    /**
     * 获取json格式的相应内容
     *
     * @param httpRequestBase
     * @return
     * @throws IOException
     */
    private static String getJsonStringResponseContent(HttpRequestBase httpRequestBase) throws IOException {
        HttpResponse response = getHttpClient().execute(httpRequestBase,
                HttpClientContext.create());
        HttpEntity responseEntity = response.getEntity();
        if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
            httpRequestBase.abort();
            return null;
        }
        return EntityUtils.toString(responseEntity, Charsets.UTF_8);
    }

    /**
     * 设置json格式的请求参数
     *
     * @param httpPost
     * @param jsonParam
     */
    public static void setJsonRequestConfig(HttpPost httpPost, String jsonParam) {
        StringEntity requestEntity = new StringEntity(jsonParam,
                Charset.forName(BaseConstants.URF8));
        requestEntity.setContentEncoding(BaseConstants.URF8);

        requestEntity.setContentType(ContentType.APPLICATION_JSON.getMimeType());
        httpPost.setEntity(requestEntity);
    }


    /**
     * 获取string形式的返回内容
     *
     * @param httpRequestBase
     * @return
     * @throws IOException
     */
    public static String getResponseStringContent(HttpRequestBase httpRequestBase) throws IOException {
        CloseableHttpResponse response = null;
        try {
            response = getHttpClient().execute(httpRequestBase,
                    HttpClientContext.create());
            HttpEntity entity = response.getEntity();
            String result = EntityUtils.toString(entity, "utf-8");
            EntityUtils.consume(entity);
            return result;
        } finally {
            IOUtils.closeQuietly(response);
        }
    }

    /**
     * 获取远程文件的长度
     *
     * @param url
     * @return
     * @throws IOException
     */
    public static long getRemoteFileLength(String url) throws IOException, HttpStateCodeNot200Or206Exception {
        String[] headerValue = getResponseHeaderValue(url, HttpHeaders.CONTENT_LENGTH);
        if (headerValue != null && headerValue.length > 0) {
            if (headerValue[0] != null) {
                return Long.valueOf(headerValue[0]);
            }
        }
        return 0;
    }


    /**
     * post请求(用于key-value格式的参数)
     * @param url
     * @param params
     * @return
     */
    public static String doPost(String url, Map params){

        BufferedReader in = null;
        try {
            // 定义HttpClient
            HttpClient client = new DefaultHttpClient();
            // 实例化HTTP方法
            HttpPost request = new HttpPost();
            request.setURI(new URI(url));

            //设置参数
            List<NameValuePair> nvps = new ArrayList<NameValuePair>();
            for (Iterator iter = params.keySet().iterator(); iter.hasNext();) {
                String name = (String) iter.next();
                String value = String.valueOf(params.get(name));
                nvps.add(new BasicNameValuePair(name, value));

                //System.out.println(name +"-"+value);
            }
            request.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));

            HttpResponse response = client.execute(request);
            int code = response.getStatusLine().getStatusCode();
            if(code == 200){    //请求成功
                in = new BufferedReader(new InputStreamReader(response.getEntity()
                        .getContent(),"utf-8"));
                StringBuffer sb = new StringBuffer("");
                String line = "";
                String NL = System.getProperty("line.separator");
                while ((line = in.readLine()) != null) {
                    sb.append(line + NL);
                }

                in.close();

                return sb.toString();
            }
            else{   //
                System.out.println("状态码：" + code);
                return null;
            }
        }
        catch(Exception e){
            e.printStackTrace();

            return null;
        }
    }

    /**
     * 获取远程相应的header的value
     *
     * @param url
     * @param headerNames header的key名称
     * @return
     */
    public static String[] getResponseHeaderValue(String url, String... headerNames) throws IOException, HttpStateCodeNot200Or206Exception {
        HttpHead httpHead = new HttpHead(url);
        httpHead.setConfig(defaultRequestConfig);
        setSimulateHeader(httpHead);
        CloseableHttpResponse response = null;
        try {
            response = getHttpClient().execute(httpHead,
                    HttpClientContext.create());
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == HttpURLConnection.HTTP_OK || statusCode == HttpURLConnection.HTTP_PARTIAL) {
                String[] headerValues = new String[headerNames.length];

                for (int i = 0; i < headerNames.length; i++) {
                    Header[] headers = response.getHeaders(headerNames[i]);
                    if (headers != null && headers.length > 0) {
                        headerValues[i] = headers[0].getValue();
                    }
                }
                return headerValues;
            }
            throw new HttpStateCodeNot200Or206Exception(url);
        } finally {
            IOUtils.closeQuietly(response);
        }
    }

    /**
     * 获取文件的最后更改日期
     *
     * @param url
     * @return
     * @throws IOException
     * @throws HttpStateCodeNot200Or206Exception
     * @throws ParseException
     */
    public static long getRemoteFileLastModified(String url) throws IOException, HttpStateCodeNot200Or206Exception, ParseException {
        String lastModified = getResponseHeaderValue(url, HttpHeaders.LAST_MODIFIED)[0];
        Date date = FastDateFormat.getInstance("E, d MMM yyyy HH:mm:ss Z", Locale.ENGLISH).parse(lastModified);
        return date.getTime();
    }

    /**
     * 获取远程服务器对应的格林威治时间
     *
     * @param url
     * @return
     */
    public static long getRemoteGMTDate(String url) throws IOException, HttpStateCodeNot200Or206Exception, ParseException {
        String remoteGMTDate = HttpClientUtil.getResponseHeaderValue(url, HttpHeaders.DATE)[0];
        Date date = FastDateFormat.getInstance("E, d MMM yyyy HH:mm:ss Z", Locale.ENGLISH).parse(remoteGMTDate);
        return date.getTime();
    }

    /**
     * 判断远程的文件是否存在
     *
     * @param url
     * @return
     */
    public static boolean isFileExists(String url) {
        try {
            long length = getRemoteFileLength(url);
            return length >= 0;
        } catch (IOException e) {
            return false;
        } catch (HttpStateCodeNot200Or206Exception e) {
            return false;
        }

    }

    /**
     * GET请求URL获取内容
     *
     * @param url
     * @return
     * @author SHANHY
     * @create 2015年12月18日
     */
    public static String get(String url) throws IOException {
        HttpGet httpget = new HttpGet(url);
        httpget.setConfig(defaultRequestConfig);
        return getResponseStringContent(httpget);
    }

    /**
     * update by xiangtao
     * 增加自定义超时时间
     * @param url
     * @param timeout
     * @return
     * @throws IOException
     */
    public static String get(String url,int timeout) throws IOException {
        HttpGet httpget = new HttpGet(url);
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(timeout)
                .setConnectTimeout(timeout).setSocketTimeout(timeout).build();
        httpget.setConfig(requestConfig);
        return getResponseStringContent(httpget);
    }

    /**
     * get请求url获取内容并转化为指定对象
     *
     * @param url
     * @param clazz
     * @param <T>
     * @return
     * @throws IOException
     */
    public static <T> T get(String url,Class<T> clazz) throws IOException {
        String data = get(url);
        T t = JsonUtils.fromJson(data, clazz);
        return t;
    }

    public static void main(String[] args) {
        String url = "http://192.168.1.15:8888/glasses/login";
        Map<String, Object> params = new HashMap<>();
        params.put("machineFingerprint", "9C:2E:A1:30:E6:91");
        try {
            String result = post(url, params);
            System.out.println(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
